home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 19 / CU Amiga Magazine's Super CD-ROM 19 (1998)(EMAP Images)(GB)[!][issue 1998-02].iso / CUCD / Programming / LEDA / source / src / graph_alg / _planar.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-16  |  17.0 KB  |  700 lines

  1. /*******************************************************************************
  2. +
  3. +  LEDA  3.1c
  4. +
  5. +
  6. +  _planar.c
  7. +
  8. +
  9. +  Copyright (c) 1994  by  Max-Planck-Institut fuer Informatik
  10. +  Im Stadtwald, 6600 Saarbruecken, FRG     
  11. +  All rights reserved.
  12. *******************************************************************************/
  13.  
  14.  
  15.  
  16. #include <LEDA/stack.h>
  17. #include <LEDA/graph.h>
  18. #include <LEDA/graph_alg.h>
  19.  
  20. static void dfs_in_make_biconnected_graph(graph & G, node v, int &dfs_count,
  21.   node_array < bool > &reached,
  22.   node_array < int >&dfsnum, node_array < int >&lowpt,
  23.   node_array < node > &parent);
  24.  
  25. const int left = 1;
  26. const int right = 2;
  27.  
  28. static void dfs_in_reorder(list < edge > &Del, node v, int &dfs_count,
  29.   node_array < bool > &reached,
  30.   node_array < int >&dfsnum,
  31.   node_array < int >&lowpt1, node_array < int >&lowpt2,
  32.   node_array < node > &parent);
  33.  
  34. class block
  35. {
  36.   private:
  37.   list < int >Latt, Ratt;    /* list of attachments */
  38.    list < edge > Lseg, Rseg;    /* list of segments represented by their *
  39.                  * defining edges */
  40.  
  41.    public:
  42.  
  43.    block(edge e, list < int >&A) {
  44.     Lseg.append(e);
  45.     Latt.conc(A);
  46. /* the other two lists are empty */
  47.   } ~block() {
  48.   }
  49.  
  50.   void flip() {
  51.     list < int >ha;
  52.      list < edge > he;
  53. /* we first interchange |Latt| and |Ratt| and then |Lseg| and |Rseg| */
  54.      ha.conc(Ratt);
  55.      Ratt.conc(Latt);
  56.      Latt.conc(ha);
  57.      he.conc(Rseg);
  58.      Rseg.conc(Lseg);
  59.      Lseg.conc(he);
  60.   } int head_of_Latt() {
  61.     return Latt.head();
  62.   }
  63.   bool empty_Latt() {
  64.     return Latt.empty();
  65.   }
  66.   int head_of_Ratt() {
  67.     return Ratt.head();
  68.   }
  69.   bool empty_Ratt() {
  70.     return Ratt.empty();
  71.   }
  72.   bool left_interlace(stack < block * >&S) {
  73. /* check for interlacing with the left side of the topmost block of
  74.  * |S| */
  75.     if (Latt.empty())
  76.       error_handler(1, "Latt is never empty");
  77.  
  78.     if (!S.empty() && !((S.top())->empty_Latt()) &&
  79.       Latt.tail() < (S.top())->head_of_Latt())
  80.       return true;
  81.     else
  82.       return false;
  83.   }
  84.   bool right_interlace(stack < block * >&S) {
  85. /* check for interlacing with the right side of the topmost block of
  86.  * |S| */
  87.     if (Latt.empty())
  88.       error_handler(1, "Latt is never empty");
  89.  
  90.     if (!S.empty() && !((S.top())->empty_Ratt()) &&
  91.       Latt.tail() < (S.top())->head_of_Ratt())
  92.       return true;
  93.     else
  94.       return false;
  95.   }
  96.   void combine(block * Bprime) {
  97. /* add block Bprime to the rear of |this| block */
  98.     Latt.conc(Bprime->Latt);
  99.     Ratt.conc(Bprime->Ratt);
  100.     Lseg.conc(Bprime->Lseg);
  101.     Rseg.conc(Bprime->Rseg);
  102.     delete(Bprime);
  103.   } bool clean(int dfsnum_w, edge_array < int >&alpha) {
  104. /* remove all attachments to |w|; there may be several */
  105.     while (!Latt.empty() && Latt.head() == dfsnum_w)
  106.       Latt.pop();
  107.     while (!Ratt.empty() && Ratt.head() == dfsnum_w)
  108.       Ratt.pop();
  109.  
  110.     if (!Latt.empty() || !Ratt.empty())
  111.       return false;
  112.  
  113. /*|Latt| and |Ratt| are empty; we record the placement of the subsegments
  114.  * in |alpha|. */
  115.  
  116.     edge e;
  117.      forall(e, Lseg) alpha[e] = left;
  118.  
  119.      forall(e, Rseg) alpha[e] = right;
  120.  
  121.      return true;
  122.   }
  123.   void add_to_Att(list < int >&Att, int dfsnum_w0, edge_array < int >&alpha) {
  124. /* add the block to the rear of |Att|. Flip if necessary */
  125.     if (!Ratt.empty() && head_of_Ratt() > dfsnum_w0)
  126.       flip();
  127.  
  128.     Att.conc(Latt);
  129.     Att.conc(Ratt);
  130. /* This needs some explanation. Note that |Ratt| is either empty
  131.  * or $\{w0\}$. Also if |Ratt| is non-empty then all subsequent sets are contained
  132.  * in $\{w0\}$. So we indeed compute an ordered set of attachments. */
  133.  
  134.     edge e;
  135.      forall(e, Lseg) alpha[e] = left;
  136.  
  137.      forall(e, Rseg) alpha[e] = right;
  138.   }
  139. };
  140.  
  141. void make_biconnected_graph(graph & G)
  142. {
  143.   node v;
  144.   node_array < bool > reached(G, false);
  145.   node u = G.first_node();
  146.  
  147.   forall_nodes(v, G) {
  148.     if (!reached[v]) {        /* explore the connected component with root
  149.                  * * |v| */
  150.       DFS(G, v, reached);
  151.       if (u != v) {        /* link |v|'s component to the first *
  152.                  * component */
  153.     G.new_edge(u, v);
  154.     G.new_edge(v, u);
  155.       }                /* end if */
  156.     }                /* end not reached */
  157.   }                /* end forall */
  158.  
  159. /* |G| is now connected. We next make it biconnected. */
  160.  
  161.   forall_nodes(v, G) reached[v] = false;
  162.   node_array < int >dfsnum(G);
  163.   node_array < node > parent(G, nil);
  164.   int dfs_count = 0;
  165.   node_array < int >lowpt(G);
  166.  
  167.   dfs_in_make_biconnected_graph(G, G.first_node(), dfs_count, reached, dfsnum, lowpt, parent);
  168.  
  169. }                /* end |make_biconnected_graph| */
  170.  
  171. static void dfs_in_make_biconnected_graph(graph & G, node v, int &dfs_count,
  172.   node_array < bool > &reached,
  173.   node_array < int >&dfsnum, node_array < int >&lowpt,
  174.   node_array < node > &parent)
  175. {
  176.  
  177.   node w;
  178.   edge e;
  179.  
  180.   dfsnum[v] = dfs_count++;
  181.   lowpt[v] = dfsnum[v];
  182.   reached[v] = true;
  183.  
  184.   if (!G.first_adj_edge(v))
  185.     return;            /* no children */
  186.  
  187.   node u = target(G.first_adj_edge(v));        /* first child */
  188.  
  189.   forall_adj_edges(e, v) {
  190.     w = target(e);
  191.     if (!reached[w]) {        /* e is a tree edge */
  192.       parent[w] = v;
  193.       dfs_in_make_biconnected_graph(G, w, dfs_count, reached, dfsnum, lowpt, parent);
  194.       if (lowpt[w] == dfsnum[v]) {    /* |v| is an articulation point. We * 
  195.                      * now add an edge. If |w| is the *
  196.                      * first child and |v| has a parent * 
  197.                      * then we connect |w| and *
  198.                      * |parent[v]|, if |w| is a first *
  199.                      * child and |v| has no parent then * 
  200.                      * we do nothing. If |w| is not the * 
  201.                      * first child then we connect |w| to 
  202.                      * 
  203.                      * * the first child. The net effect
  204.                      * of  * all of this is to link all * 
  205.                      * children of an articulation point
  206.                      * * to the first child and the first
  207.                      * * child to the parent (if it
  208.                      * exists)  */
  209.     if (w == u && parent[v]) {
  210.       G.new_edge(w, parent[v]);
  211.       G.new_edge(parent[v], w);
  212.     }
  213.     if (w != u) {
  214.       G.new_edge(u, w);
  215.       G.new_edge(w, u);
  216.     }
  217.       }                /* end if lowpt = dfsnum */
  218.       lowpt[v] = Min(lowpt[v], lowpt[w]);
  219.     }                /* end tree edge */
  220.     else
  221.       lowpt[v] = Min(lowpt[v], dfsnum[w]);    /* non tree edge */
  222.   }                /* end forall */
  223. }                /* end dfs */
  224.  
  225. static void reorder(graph & G, node_array < int >&dfsnum,
  226.   node_array < node > &parent)
  227. {
  228.   node v;
  229.   node_array < bool > reached(G, false);
  230.   int dfs_count = 0;
  231.   list < edge > Del;
  232.   node_array < int >lowpt1(G), lowpt2(G);
  233.  
  234.   dfs_in_reorder(Del, G.first_node(), dfs_count, reached, dfsnum, lowpt1, lowpt2, parent);
  235.  
  236. /* remove forward and reversals of tree edges */
  237.  
  238.   edge e;
  239.   forall(e, Del) G.del_edge(e);
  240.  
  241. /* we now reorder adjacency lists as described in \cite[page 101]{Me:book} */
  242.  
  243.   node w;
  244.   edge_array < int >cost(G);
  245.   forall_edges(e, G) {
  246.     v = source(e);
  247.     w = target(e);
  248.     cost[e] = ((dfsnum[w] < dfsnum[v]) ?
  249.       2 * dfsnum[w] :
  250.       ((lowpt2[w] >= dfsnum[v]) ?
  251.     2 * lowpt1[w] : 2 * lowpt1[w] + 1));
  252.   }
  253.  
  254.   G.sort_edges(cost);
  255.  
  256. }
  257.  
  258. static void dfs_in_reorder(list < edge > &Del, node v, int &dfs_count,
  259.   node_array < bool > &reached,
  260.   node_array < int >&dfsnum,
  261.   node_array < int >&lowpt1, node_array < int >&lowpt2,
  262.   node_array < node > &parent)
  263. {
  264.   node w;
  265.   edge e;
  266.  
  267.   dfsnum[v] = dfs_count++;
  268.   lowpt1[v] = lowpt2[v] = dfsnum[v];
  269.   reached[v] = true;
  270.   forall_adj_edges(e, v) {
  271.     w = target(e);
  272.     if (!reached[w]) {        /* e is a tree edge */
  273.       parent[w] = v;
  274.       dfs_in_reorder(Del, w, dfs_count, reached, dfsnum, lowpt1, lowpt2,
  275.     parent);
  276.       lowpt1[v] = Min(lowpt1[v], lowpt1[w]);
  277.     }                /* end tree edge */
  278.     else {
  279.       lowpt1[v] = Min(lowpt1[v], dfsnum[w]);    /* no effect for forward *
  280.                          * edges */
  281.       if ((dfsnum[w] >= dfsnum[v]) || w == parent[v])
  282. /* forward edge or reversal of tree edge */
  283.     Del.append(e);
  284.     }                /* end non-tree edge */
  285.  
  286.   }                /* end forall */
  287.  
  288. /* we know |lowpt1[v]| at this point and now make a second pass over all
  289.  * adjacent edges of |v| to compute |lowpt2| */
  290.  
  291.   {
  292.     forall_adj_edges(e, v) {
  293.       w = target(e);
  294.       if (parent[w] == v) {    /* tree edge */
  295.     if (lowpt1[w] != lowpt1[v])
  296.       lowpt2[v] = Min(lowpt2[v], lowpt1[w]);
  297.     lowpt2[v] = Min(lowpt2[v], lowpt2[w]);
  298.       }                /* end tree edge */
  299.       else
  300.       /* all other edges */ if (lowpt1[v] != dfsnum[w])
  301.     lowpt2[v] = Min(lowpt2[v], dfsnum[w]);
  302.     }                /* end forall */
  303.   }
  304. }                /* end dfs */
  305.  
  306. static bool strongly_planar(edge e0, graph & G, list < int >&Att,
  307.   edge_array < int >&alpha,
  308.   node_array < int >&dfsnum,
  309.   node_array < node > &parent)
  310. {
  311.  
  312.   node x = source(e0);
  313.  
  314.   node y = target(e0);
  315.  
  316.   edge e = G.first_adj_edge(y);
  317.  
  318.   node wk = y;
  319.  
  320.   while (dfsnum[target(e)] > dfsnum[wk]) {    /* e is a tree edge */
  321.     wk = target(e);
  322.     e = G.first_adj_edge(wk);
  323.   }
  324.  
  325.   node w0 = target(e);
  326.  
  327.   node w = wk;
  328.  
  329.   stack < block * >S;
  330.  
  331.   while (w != x) {
  332.     int count = 0;
  333.     forall_adj_edges(e, w) {
  334.       count++;
  335.       if (count != 1) {        /* no action for first edge */
  336.     list < int >A;
  337.  
  338.     if (dfsnum[w] < dfsnum[target(e)]) {    /* tree edge */
  339.       if (!strongly_planar(e, G, A, alpha, dfsnum, parent)) {
  340.         while (!S.empty())
  341.           delete(S.pop());
  342.         return false;
  343.       }
  344.  
  345.     }
  346.     else
  347.       A.append(dfsnum[target(e)]);    /* a back edge */
  348.  
  349.     block *B = new block(e, A);
  350.  
  351.     while (true) {
  352.       if (B->left_interlace(S))
  353.         (S.top())->flip();
  354.  
  355.       if (B->left_interlace(S)) {
  356.         delete(B);
  357.         while (!S.empty())
  358.           delete(S.pop());
  359.         return false;
  360.       };
  361.  
  362.       if (B->right_interlace(S))
  363.         B->combine(S.pop());
  364.       else
  365.         break;
  366.     }            /* end while */
  367.  
  368.     S.push(B);
  369.  
  370.       }                /* end if */
  371.     }                /* end forall */
  372.  
  373.     while (!S.empty() && (S.top())->clean(dfsnum[parent[w]], alpha))
  374.       delete(S.pop());
  375.  
  376.     w = parent[w];
  377.   }                /* end while */
  378.  
  379.   Att.clear();
  380.   while (!S.empty()) {
  381.     block *B = S.pop();
  382.  
  383.     if (!(B->empty_Latt()) && !(B->empty_Ratt()) &&
  384.       (B->head_of_Latt() > dfsnum[w0]) && (B->head_of_Ratt() > dfsnum[w0])) {
  385.       delete(B);
  386.       while (!S.empty())
  387.     delete(S.pop());
  388.       return false;
  389.     }
  390.  
  391.     B->add_to_Att(Att, dfsnum[w0], alpha);
  392.  
  393.     delete(B);
  394.   }                /* end while */
  395.  
  396. /* Let's not forget (as the book does) that $w0$ is an attachment of $S(e0)$
  397.  * except if $w0 = x$. */
  398.  
  399.   if (w0 != x)
  400.     Att.append(dfsnum[w0]);
  401.  
  402.   return true;
  403. }
  404.  
  405. static void embedding(edge e0, int t, GRAPH < node, edge > &G,
  406.   edge_array < int >&alpha,
  407.   node_array < int >&dfsnum,
  408.   list < edge > &T, list < edge > &A, int &cur_nr,
  409.   edge_array < int >&sort_num, node_array < edge > &tree_edge_into,
  410.   node_array < node > &parent, edge_array < edge > &reversal)
  411. {
  412.  
  413.   node x = source(e0);
  414.  
  415.   node y = target(e0);
  416.  
  417.   tree_edge_into[y] = e0;
  418.  
  419.   edge e = G.first_adj_edge(y);
  420.  
  421.   node wk = y;
  422.  
  423.   while (dfsnum[target(e)] > dfsnum[wk]) {    /* e is a tree edge */
  424.     wk = target(e);
  425.     tree_edge_into[wk] = e;
  426.     e = G.first_adj_edge(wk);
  427.   }
  428.  
  429.   node w0 = target(e);
  430.   edge back_edge_into_w0 = e;
  431.  
  432.   node w = wk;
  433.  
  434.   list < edge > Al, Ar;
  435.   list < edge > Tprime, Aprime;
  436.  
  437.   T.clear();
  438.   T.append(G[e]);        /* |e = (wk,w0)| at this point, line (2) */
  439.  
  440.   while (w != x) {
  441.     int count = 0;
  442.     forall_adj_edges(e, w) {
  443.       count++;
  444.       if (count != 1) {        /* no action for first edge */
  445.  
  446.     if (dfsnum[w] < dfsnum[target(e)]) {    /* tree edge */
  447.       int tprime = ((t == alpha[e]) ? left : right);
  448.       embedding(e, tprime, G, alpha, dfsnum, Tprime, Aprime, cur_nr, sort_num, tree_edge_into, parent, reversal);
  449.  
  450.     }
  451.     else {            /* back edge */
  452.       Tprime.append(G[e]);    /* $e$ */
  453.       Aprime.append(reversal[G[e]]);    /* reversal of $e$ */
  454.     }
  455.  
  456.     if (t == alpha[e]) {
  457.       Tprime.conc(T);
  458.       T.conc(Tprime);    /* $T = Tprime\ conc\ T$ */
  459.  
  460.       Al.conc(Aprime);    /* $Al = Al\ conc\ Aprime$ */
  461.  
  462.     }
  463.     else {
  464.       T.conc(Tprime);    /* $ T\ = T\ conc\ Tprime $ */
  465.  
  466.       Aprime.conc(Ar);
  467.       Ar.conc(Aprime);    /* $ Ar\ = Aprime\ conc\ Ar$ */
  468.  
  469.     }
  470.  
  471.       }                /* end if */
  472.     }                /* end forall */
  473.  
  474.     T.append(reversal[G[tree_edge_into[w]]]);    /* $(w_{j-1},w_j)$ */
  475.  
  476.     forall(e, T) sort_num[e] = cur_nr++;
  477.  
  478. /* |w|'s adjacency list is now computed; we clear |T| and prepare for the
  479.  * next iteration by moving all darts incident to |parent[w]| from |Al| and
  480.  * |Ar| to |T|. These darts are at the rear end of |Al| and at the front end
  481.  * of |Ar| */
  482.  
  483.     T.clear();
  484.  
  485.     while (!Al.empty() && source(Al.tail()) == G[parent[w]])
  486. /* |parent[w]| is in |G|, |Al.tail| in |H| */
  487.     {
  488.       T.push(Al.Pop());        /* Pop removes from the rear */
  489.     }
  490.  
  491.     T.append(G[tree_edge_into[w]]);    /* push would be equivalent */
  492.  
  493.     while (!Ar.empty() && source(Ar.head()) == G[parent[w]]) {    /* */
  494.       T.append(Ar.pop());    /* pop removes from the front */
  495.     }
  496.  
  497.     w = parent[w];
  498.   }                /* end while */
  499.  
  500.   A.clear();
  501.  
  502.   A.conc(Ar);
  503.   A.append(reversal[G[back_edge_into_w0]]);
  504.   A.conc(Al);
  505.  
  506. }
  507.  
  508. bool PLANAR(graph & Gin, bool embed)
  509. /* |Gin| is a directed graph. |planar| decides whether |Gin| is planar.
  510.  * If it is and |embed == true| then it also computes a
  511.  * combinatorial embedding of |Gin| by suitably reordering
  512.  * its adjacency lists.
  513.  * |Gin| must be bidirected in that case. */
  514.  
  515. {
  516.   int n = Gin.number_of_nodes();
  517.  
  518.   if (n <= 3)
  519.     return true;
  520.  
  521.   if (Gin.number_of_edges() > 6 * n - 12)
  522.     return false;
  523.  
  524. /* An undirected planar graph has at most $3n-6$ edges; a directed graph may
  525.  * have twice as many */
  526.  
  527.   GRAPH < node, edge > G;
  528.  
  529.   edge_array < edge > companion_in_G(Gin);
  530.  
  531.   node_array < node > link(Gin);
  532.  
  533.   bool Gin_is_bidirected = true;
  534.  
  535.   {
  536.     node v;
  537.     forall_nodes(v, Gin) link[v] = G.new_node(v);    /* bidirectional *
  538.                              * links */
  539.     edge e;
  540.     forall_edges(e, Gin) companion_in_G[e] =
  541.       G.new_edge(link[source(e)], link[target(e)], e);
  542.  
  543.   }
  544.  
  545.   {
  546.     node_array < int >nr(G);
  547.     edge_array < int >cost(G);
  548.     int cur_nr = 0;
  549.     int n = G.number_of_nodes();
  550.     node v;
  551.     edge e;
  552.  
  553.     forall_nodes(v, G) nr[v] = cur_nr++;
  554.  
  555.     forall_edges(e, G) cost[e] = ((nr[source(e)] < nr[target(e)]) ?
  556.       n * nr[source(e)] + nr[target(e)] :
  557.       n * nr[target(e)] + nr[source(e)]);
  558.  
  559.     G.sort_edges(cost);
  560.  
  561.     list < edge > L = G.all_edges();
  562.  
  563.     while (!L.empty()) {
  564.       e = L.pop();
  565. /* check whether the first edge on |L| is equal to the reversal of |e|. If so,
  566.  * delete it from |L|, if not, add the reversal to |G| */
  567.       if (!L.empty() && (source(e) == target(L.head())) && (source(L.head()) == target(e)))
  568.     L.pop();
  569.       else {
  570.     G.new_edge(target(e), source(e));
  571.     Gin_is_bidirected = false;
  572.       }
  573.     }
  574.  
  575.   }
  576.  
  577.   make_biconnected_graph(G);
  578.  
  579.   GRAPH < node, edge > H;
  580.   edge_array < edge > companion_in_H(Gin);
  581.  
  582.   {
  583.     node v;
  584.     forall_nodes(v, G) G.assign(v, H.new_node(v));
  585.  
  586.     edge e;
  587.     forall_edges(e, G) G.assign(e, H.new_edge(G[source(e)], G[target(e)], e));
  588.  
  589.     edge ein;
  590.     forall_edges(ein, Gin) companion_in_H[ein] = G[companion_in_G[ein]];
  591.   }
  592.  
  593.   edge_array < edge > reversal(H);
  594.  
  595.   compute_correspondence(H, reversal);
  596.  
  597.   node_array < int >dfsnum(G);
  598.   node_array < node > parent(G, nil);
  599.  
  600.   reorder(G, dfsnum, parent);
  601.  
  602.   edge_array < int >alpha(G, 0);
  603.  
  604.   {
  605.     list < int >Att;
  606.  
  607.     alpha[G.first_adj_edge(G.first_node())] = left;
  608.  
  609.     if (!strongly_planar(G.first_adj_edge(G.first_node()), G, Att, alpha, dfsnum, parent))
  610.       return false;
  611.   }
  612.  
  613.   if (embed) {
  614.     if (Gin_is_bidirected) {
  615.       list < edge > T, A;    /* lists of edges of |H| */
  616.  
  617.       int cur_nr = 0;
  618.       edge_array < int >sort_num(H);
  619.       node_array < edge > tree_edge_into(G);
  620.  
  621.       embedding(G.first_adj_edge(G.first_node()), left, G, alpha, dfsnum, T, A,
  622.     cur_nr, sort_num, tree_edge_into, parent, reversal);
  623.  
  624. /* |T| contains all edges incident to the first node except the cycle edge into it.
  625.  * That edge comprises |A| */
  626.  
  627.       T.conc(A);
  628.       edge e;
  629.  
  630.       forall(e, T) sort_num[e] = cur_nr++;
  631.  
  632.       edge_array < int >sort_Gin(Gin);
  633.  
  634.       {
  635.     edge ein;
  636.     forall_edges(ein, Gin) sort_Gin[ein] = sort_num[companion_in_H[ein]];
  637.       }
  638.  
  639.       Gin.sort_edges(sort_Gin);
  640.     }
  641.  
  642.     else
  643.       error_handler(2, "sorry: can only embed bidirected graphs");
  644.   }
  645.   return true;
  646. }
  647.  
  648. bool PLANAR(graph & G, list < edge > &P, bool embed)
  649. {
  650.   if (PLANAR(G, embed))
  651.     return true;
  652.  
  653. /* We work on a copy |H| of |G| since the procedure alters |G|; we link every
  654.  * vertex and edge of |H| with its original. For the vertices we also have the
  655.  * reverse links. */
  656.  
  657.   GRAPH < node, edge > H;
  658.   node_array < node > link(G);
  659.   node v;
  660.   forall_nodes(v, G) link[v] = H.new_node(v);
  661. /* This requires some explanation. |H.new_node(v)| adds a new node to
  662.  * |H|, returns the new node, and makes |v| the information associated
  663.  * with the new node. So the statement creates a copy of |v| and
  664.  * bidirectionally links it with |v| */
  665.  
  666.   edge e;
  667.   forall_edges(e, G) H.new_edge(link[source(e)], link[target(e)], e);
  668. /* |link[source(e)]| and |link[target(e)]| are the copies of |source(e)|
  669.  * and |target(e)| in |H|. The operation |H.new_edge| creates a new edge
  670.  * with these endpoints, returns it, and makes |e| the information of that
  671.  * edge. So the effect of the loop is to make the edge set of |H| a copy
  672.  * of the edge set of |G| and to let every edge of |H| know its original.
  673.  * We can now determine a minimal non-planar subgraph of |H| */
  674.   list < edge > L = H.all_edges();
  675.  
  676.   edge eh;
  677.  
  678.   forall(eh, L) {
  679.     e = H[eh];            /* the edge in |G| corresponding to |eh| */
  680.     node x = source(eh);
  681.     node y = target(eh);
  682.     H.del_edge(eh);
  683.     if (PLANAR(H))
  684.       H.new_edge(x, y, e);    /* put a new version of |eh| back in and *
  685.                  * establish the correspondence */
  686.  
  687.   }
  688.  
  689. /* |H| is now a homeomorph of either $K_5$ or $K_{3,3}$. We still need
  690.  * to translate back to |G|. */
  691.  
  692.   P.clear();
  693.  
  694.   forall_edges(eh, H) P.append(H[eh]);
  695.  
  696.   return false;
  697.  
  698. }
  699.